import socket, threading, queue, json, time, ctypes
from config import config
from msg import Msg
from log import logger

class Script(threading.Thread):
    def __init__(self, profile, dll, deviceList, scriptPath, file, isLoop) -> None:
        super().__init__()

        self.profile = profile
        self.dll = dll
        self.deviceList = deviceList
        self.file = file
        self.scriptPath = scriptPath
        
        
        self.scriptIndex = 0
        self.retryCounter = 0
        self.deviceIndex = 0            

        self.hasGotExitSignal = False   
        self.daemon = True

        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.dstServer = ("localhost", config["forwardServer"]["port"])

        self.isLoop = isLoop
        self.runTimes = 1

        self.queueScriptRx = queue.Queue()   # 某些脚本需要脚本进行回复确认
        self.exitReason = ""

    def check(self):
        # 检查是否能打开脚本
        with open(self.scriptPath, "r") as f:
            try:
                self.script = json.loads(f.read())
            except Exception:
                return False, "文件格式错误"
        
        # 检查脚本内容
        self.scripts = self.script.get("script")
        if self.script.get("script") is None:
            return False, "找不到script内容"
        else:
            for script in self.scripts:
                if script.get("cmd") is None:
                    return False, "脚本中未填写命令, 无法执行"

        # 
        needFile = self.script.get("needFile")
        if needFile is not None and needFile is True:
            if self.file is None:
                return False, "该脚本需要文件, 但是并未添加文件"
            else:
                try: 
                    f = open(self.file, "rb")
                except Exception:
                    return False, "找不到指定文件"
                f.close()

        return True, ""
    
    # 线程是否活跃
    def isAlive(self):
        return self.is_alive()
    
    # 退出线程
    def exit(self):
        self.hasGotExitSignal = True

    # 查找命令
    def findCmd(self, cmdType, cmdTarget):
        for cmd in self.profile["cmdList"].get(cmdType):
            if cmd.get("cmd") == cmdTarget:
                return cmd
        return None
    
    def hex(self, packect):
        hexString = ""
        for i in packect:
            hexString += "%02X " % i
        return (hexString)


    # 线程执行内容
    def run(self):
        logger.info("[Script] Start")
        err, errInfo = self.check()

        if err is False:
            logger.info("[Script] Exit, script check failed, info is %s" % errInfo)

        self.socket.connect(self.dstServer)

        # 依次处理脚本
        while((self.isLoop or self.runTimes > 0) and self.hasGotExitSignal == False):
            self.runTimes -= 1
            
            for deviceId in self.deviceList:
                for index in range(len(self.scripts)):
                    
                    if self.hasGotExitSignal == True:
                        break

                    # 获取脚本以及对应命令
                    script = self.scripts[index]
                    cmdAttribute = self.findCmd("send", script["cmd"])
                    if cmdAttribute is None:
                        logger.info("[Script] cant find cmd %s in profile" % script["cmd"])
                        continue

                    # 延迟
                    delay = script.get("delay")
                    if delay is None:
                        delay = 0

                    # 发送处理
                    needFile = script.get("needFile")
                    if needFile is not None and needFile is True:
                        # 检查当前发送指令是否为多包发送
                        if cmdAttribute.get("multiPacket") is not None and cmdAttribute.get("multiPacket") is True:
                            with open(self.file, "rb") as f:
                                entieFile = f.read()      # 文件全部读出

                            contentSize = cmdAttribute.get("contentSize")
                            packetSize = cmdAttribute.get("packetSize")
                            
                            # 将文件拆解成N份, 并创建 N * PacketSize 大小的数组
                            packetCount = len(entieFile) / contentSize
                            packetCount = int(packetCount) if (len(entieFile) % contentSize) == 0 else (int(packetCount) + 1)

                            msg = Msg(deviceId, script["cmd"], script.get("tag"))
                            msg.complete(self.profile, "send")

                            packetDst = (ctypes.c_uint8 * (packetSize * packetCount))()
                            packetSrc = (ctypes.c_uint8 * len(entieFile))()
                            for i in range(len(entieFile)):
                                packetSrc[i] = entieFile[i]
                            msgInfoString = ctypes.create_string_buffer(msg.jsonString().encode("utf8"))

                            packetsLen = self.dll.createMultiTxMsg( packetDst, 
                                                                    packetSize * packetCount,
                                                                    deviceId,
                                                                    packetSrc,
                                                                    len(packetSrc),
                                                                    msgInfoString)
                            
                            # 获取多包发送间隔
                            interval = script.get("interval")
                            if interval is None:
                                interval = 0
                        
                            msg.addKeyValue("packetCount", packetCount)

                            # 发送多包
                            for i in range(packetCount):
                                if self.hasGotExitSignal == True:
                                    break

                                logger.info(msg.jsonString())

                                msg.addKeyValue("currentIndex", i)
                                
                                if i == (packetCount - 1):
                                    packet = bytes(packetDst)[i * packetSize: packetsLen]
                                else:
                                    packet = bytes(packetDst)[i * packetSize: (i+1) * packetSize]

                                msg.addKeyValue("origin", [int(i) for i in packet])
                                self.socket.send(msg.jsonString().encode("utf8"))

                                if i < 2:
                                    time.sleep(5)
                                else:
                                    time.sleep(interval / 1000)
        
                    else:
                        # 根据profile以及参数生成cmd以及string
                        msg = Msg(deviceId, script["cmd"], script.get("tag"))
                        if script.get("cmdValue"):
                            msg.setCmdValue(script.get("cmdValue"))
                        if script.get("baseValue"):
                            msg.setBaseValue(script.get("baseValue"))
                        
                        # 发送数据并延迟
                        retransCount = script.get("retry")
                        if retransCount is None:
                            retransCount = 1

                        # 发送前清空接收队列
                        while not self.queueScriptRx.empty():
                            self.queueScriptRx.get()

                        for i in range(retransCount):
                            self.socket.send(msg.jsonString().encode("utf8"))
                            time.sleep(delay / 1000)
                            
                            # 脚本应答退出机制
                            hasGotRightCmd = False
                            if script.get("needRsp") is not None:
                                # 没有回复直接退出
                                if self.queueScriptRx.empty():
                                    self.exitReason = "没有收到回复"
                                    self.hasGotExitSignal = True
                                    break
                                # 有回复要检查是否与指定的相同
                                while not self.queueScriptRx.empty():
                                    rxMsgInfo = self.queueScriptRx.get()
                                    if script["needRsp"]["cmd"] == rxMsgInfo["cmd"]:
                                        hasGotRightCmd = True
                                if hasGotRightCmd == False:
                                    self.exitReason = "没有收到指定条件的报文"
                                    self.hasGotExitSignal = True
                                    break

                            # 判断是否退出脚本
                            if self.hasGotExitSignal == True:
                                break

                        if self.hasGotExitSignal == True:
                                break


                    time.sleep(delay / 1000)

        self.socket.close()

        logger.info("[Script] Exit")
    
